home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / pyshared / DistUpgrade / DistUpgradeController.py < prev    next >
Text File  |  2009-11-02  |  78KB  |  1,645 lines

  1. # DistUpgradeController.py 
  2. #  
  3. #  Copyright (c) 2004-2008 Canonical
  4. #  
  5. #  Author: Michael Vogt <michael.vogt@ubuntu.com>
  6. #  This program is free software; you can redistribute it and/or 
  7. #  modify it under the terms of the GNU General Public License as 
  8. #  published by the Free Software Foundation; either version 2 of the
  9. #  License, or (at your option) any later version.
  10. #  This program is distributed in the hope that it will be useful,
  11. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. #  GNU General Public License for more details.
  14. #  You should have received a copy of the GNU General Public License
  15. #  along with this program; if not, write to the Free Software
  16. #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
  17. #  USA
  18.  
  19.  
  20. import warnings
  21. warnings.filterwarnings("ignore", "apt API not stable yet", FutureWarning)
  22. import apt
  23. import apt_pkg
  24. import sys
  25. import os
  26. import subprocess
  27. import logging
  28. import re
  29. import statvfs
  30. import shutil
  31. import glob
  32. import time
  33. import copy
  34. import ConfigParser
  35. from stat import *
  36. from utils import country_mirror, url_downloadable, check_and_fix_xbit
  37. from string import Template
  38.  
  39.  
  40. import DistUpgradeView
  41. from DistUpgradeConfigParser import DistUpgradeConfig
  42. from DistUpgradeFetcherCore import country_mirror
  43. from DistUpgradeQuirks import DistUpgradeQuirks
  44. from DistUpgradeAptCdrom import AptCdrom
  45. from DistUpgradeAufs import setupAufs, aufsOptionsAndEnvironmentSetup
  46.  
  47. from sourceslist import SourcesList, SourceEntry, is_mirror
  48. from distro import Distribution, get_distro, NoDistroTemplateException
  49.  
  50. from DistUpgradeGettext import gettext as _
  51. from DistUpgradeGettext import ngettext
  52. import gettext
  53.  
  54. from DistUpgradeCache import *
  55. from DistUpgradeApport import *
  56.  
  57.  
  58. class NoBackportsFoundException(Exception):
  59.     pass
  60.  
  61.  
  62. class DistUpgradeController(object):
  63.     """ this is the controller that does most of the work """
  64.     
  65.     def __init__(self, distUpgradeView, options=None, datadir=None):
  66.         # setup the paths
  67.         localedir = "/usr/share/locale/"
  68.         if datadir == None:
  69.             datadir = os.getcwd()
  70.             localedir = os.path.join(datadir,"mo")
  71.             gladedir = datadir
  72.         self.datadir = datadir
  73.         self.options = options
  74.  
  75.         # init gettext
  76.         gettext.bindtextdomain("update-manager",localedir)
  77.         gettext.textdomain("update-manager")
  78.  
  79.         # setup the view
  80.         logging.debug("Using '%s' view" % distUpgradeView.__class__.__name__)
  81.         self._view = distUpgradeView
  82.         self._view.updateStatus(_("Reading cache"))
  83.         self.cache = None
  84.  
  85.         if not self.options or self.options.withNetwork == None:
  86.             self.useNetwork = True
  87.         else:
  88.             self.useNetwork = self.options.withNetwork
  89.         if options:
  90.             cdrompath = options.cdromPath
  91.         else:
  92.             cdrompath = None
  93.         self.aptcdrom = AptCdrom(distUpgradeView, cdrompath)
  94.  
  95.         # the configuration
  96.         self.config = DistUpgradeConfig(datadir)
  97.         self.sources_backup_ext = "."+self.config.get("Files","BackupExt")
  98.  
  99.         # move some of the options stuff into the self.config, 
  100.         # ConfigParser deals only with strings it seems *sigh*
  101.         self.config.add_section("Options")
  102.         self.config.set("Options","withNetwork", str(self.useNetwork))
  103.  
  104.         # aufs stuff
  105.         aufsOptionsAndEnvironmentSetup(self.options, self.config)
  106.  
  107.         # some constants here
  108.         self.fromDist = self.config.get("Sources","From")
  109.         self.toDist = self.config.get("Sources","To")
  110.         self.origin = self.config.get("Sources","ValidOrigin")
  111.         self.arch = apt_pkg.Config.Find("APT::Architecture")
  112.  
  113.         # we run with --force-overwrite by default
  114.         if not os.environ.has_key("RELEASE_UPGRADE_NO_FORCE_OVERWRITE"):
  115.             logging.debug("enable dpkg --force-overwrite")
  116.             apt_pkg.Config.Set("DPkg::Options::","--force-overwrite")
  117.  
  118.         # we run in full upgrade mode by default
  119.         self._partialUpgrade = False
  120.         
  121.         # install the quirks handler
  122.         self.quirks = DistUpgradeQuirks(self, self.config)
  123.  
  124.         # setup env var 
  125.         os.environ["RELEASE_UPGRADE_IN_PROGRESS"] = "1"
  126.         os.environ["PYCENTRAL_NO_DPKG_QUERY"] = "1"
  127.         os.environ["PYCENTRAL_FORCE_OVERWRITE"] = "1"
  128.         os.environ["PATH"] = "%s:%s" % (os.getcwd()+"/imported",
  129.                                         os.environ["PATH"])
  130.         check_and_fix_xbit("./imported/invoke-rc.d")
  131.  
  132.         # set max retries
  133.         maxRetries = self.config.getint("Network","MaxRetries")
  134.         apt_pkg.Config.Set("Acquire::Retries", str(maxRetries))
  135.         # max sizes for dpkgpm for large installs (see linux/limits.h and 
  136.         #                                          linux/binfmts.h)
  137.         apt_pkg.Config.Set("Dpkg::MaxArgs", str(64*1024))
  138.         apt_pkg.Config.Set("Dpkg::MaxArgBytes", str(128*1024))
  139.  
  140.         # smaller to avoid hangs
  141.         apt_pkg.Config.Set("Acquire::http::Timeout","20")
  142.         apt_pkg.Config.Set("Acquire::ftp::Timeout","20")
  143.  
  144.         # no list cleanup here otherwise a "cancel" in the upgrade
  145.         # will not restore the full state (lists will be missing)
  146.         apt_pkg.Config.Set("Apt::Get::List-Cleanup", "false")
  147.  
  148.         # forced obsoletes
  149.         self.forced_obsoletes = self.config.getlist("Distro","ForcedObsoletes")
  150.         # list of valid mirrors that we can add
  151.         self.valid_mirrors = self.config.getListFromFile("Sources","ValidMirrors")
  152.         # debugging
  153.         #apt_pkg.Config.Set("DPkg::Options::","--debug=0077")
  154.  
  155.         # apt cron job
  156.         self._aptCronJobPerms = 0755
  157.  
  158.     def openCache(self, lock=True):
  159.         logging.debug("openCache()")
  160.         if self.cache is not None:
  161.             self.cache.releaseLock()
  162.             self.cache.unlockListsDir()
  163.         try:
  164.             self.cache = MyCache(self.config,
  165.                                  self._view,
  166.                                  self.quirks,
  167.                                  self._view.getOpCacheProgress(),
  168.                                  lock)
  169.             # alias name for the plugin interface code
  170.             self.apt_cache = self.cache
  171.         # if we get a dpkg error that it was interrupted, just
  172.         # run dpkg --configure -a
  173.         except CacheExceptionDpkgInterrupted, e:
  174.             logging.warning("dpkg interrupted, calling dpkg --configure -a")
  175.             self._view.getTerminal().call(["dpkg","--configure","-a"])
  176.             self.cache = MyCache(self.config,
  177.                                  self._view,
  178.                                  self.quirks,
  179.                                  self._view.getOpCacheProgress())
  180.         except CacheExceptionLockingFailed, e:
  181.             logging.error("Cache can not be locked (%s)" % e)
  182.             self._view.error(_("Unable to get exclusive lock"),
  183.                              _("This usually means that another "
  184.                                "package management application "
  185.                                "(like apt-get or aptitude) "
  186.                                "already running. Please close that "
  187.                                "application first."));
  188.             sys.exit(1)
  189.         self.cache.partialUpgrade = self._partialUpgrade
  190.         logging.debug("/openCache(), new cache size %i" % len(self.cache))
  191.  
  192.     def _isRemoteLogin(self):
  193.         " check if we are running form a remote login "
  194.         # easy
  195.         if (os.environ.has_key("SSH_CONNECTION") or
  196.             os.environ.has_key("SSH_TTY")):
  197.             return True
  198.         # sudo cleans out SSH_ environment
  199.         out = subprocess.Popen(["who","-m","--ips"],stdout=subprocess.PIPE).communicate()[0]
  200.         logging.debug("who -m --ips: '%s'" % out)
  201.         # FIXME: what about IPv6 ?
  202.         # do regexp search for a IP 
  203.         if re.search("(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$", out):
  204.             return True
  205.         return False
  206.  
  207.     def _viewSupportsSSH(self):
  208.       """
  209.       Returns True if this view support upgrades over ssh.
  210.       In theory all views should support it, but for savety
  211.       we do only allow text ssh upgrades (see LP: #322482)
  212.       """
  213.       supported = self.config.getlist("View","SupportSSH")
  214.       if self._view.__class__.__name__ in supported:
  215.           return True
  216.       return False
  217.  
  218.     def _sshMagic(self):
  219.         """ this will check for server mode and if we run over ssh.
  220.             if this is the case, we will ask and spawn a additional
  221.             daemon (to be sure we have a spare one around in case
  222.             of trouble)
  223.         """
  224.         pidfile = os.path.join("/var/run/release-upgrader-sshd.pid")
  225.         if (not os.path.exists(pidfile) and self._isRemoteLogin()):
  226.             # check if the frontend supports ssh upgrades (see lp: #322482)
  227.             if not self._viewSupportsSSH():
  228.                 logging.error("upgrade over ssh not alllowed")
  229.                 self._view.error(_("Upgrading over remote connection not supported"),
  230.                                  _("You are running the upgrade over a "
  231.                                    "remote ssh connection with a frontend "
  232.                                    "that does "
  233.                                    "not support this. The upgrade will "
  234.                                    "abort now. Please try without ssh.")
  235.                                  )
  236.                 sys.exit(1)
  237.                 return False
  238.             # ask for a spare one to start
  239.             port = 9004
  240.             res = self._view.askYesNoQuestion(
  241.                 _("Continue running under SSH?"),
  242.                 _("This session appears to be running under ssh. "
  243.                   "It is not recommended to perform a upgrade "
  244.                   "over ssh currently because in case of failure "
  245.                 "it is harder to recover.\n\n"
  246.                   "If you continue, a additional ssh daemon will be "
  247.                   "started at port '%s'.\n"
  248.                   "Do you want to continue?") % port)
  249.             # abort
  250.             if res == False:
  251.                 sys.exit(1)
  252.             res = subprocess.call(["/usr/sbin/sshd",
  253.                                    "-o", "PidFile=%s" % pidfile,
  254.                                    "-p",str(port)])
  255.             if res == 0:
  256.                 self._view.information(
  257.                     _("Starting additional sshd"),
  258.                     _("To make recovery in case of failure easier, an "
  259.                       "additional sshd will be started on port '%s'. "
  260.                       "If anything goes wrong with the running ssh "
  261.                       "you can still connect to the additional one.\n"
  262.                       ) % port)
  263.         return True
  264.  
  265.     def _tryUpdateSelf(self):
  266.         """ this is a helper that is run if we are started from a CD
  267.             and we have network - we will then try to fetch a update
  268.             of ourself
  269.         """  
  270.         from MetaRelease import MetaReleaseCore
  271.         from DistUpgradeFetcherSelf import DistUpgradeFetcherSelf
  272.         # check if we run from a LTS 
  273.         forceLTS=False
  274.         if (self.release == "dapper" or
  275.             self.release == "hardy"):
  276.             forceLTS=True
  277.         m = MetaReleaseCore(useDevelopmentRelease=False,
  278.                             forceLTS=forceLTS)
  279.         # this will timeout eventually
  280.         while m.downloading:
  281.             self._view.processEvents()
  282.             time.sleep(0.1)
  283.         if m.new_dist is None:
  284.             logging.error("No new dist found")
  285.             return False
  286.         # we have a new dist
  287.         progress = self._view.getFetchProgress()
  288.         fetcher = DistUpgradeFetcherSelf(new_dist=m.new_dist,
  289.                                          progress=progress,
  290.                                          options=self.options,
  291.                                          view=self._view)
  292.         fetcher.run()
  293.  
  294.     def _pythonSymlinkCheck(self):
  295.         """ sanity check that /usr/bin/python points to the default
  296.             python version. Users tend to modify this symlink, which
  297.             breaks stuff in obscure ways (Ubuntu #75557).
  298.         """
  299.         logging.debug("_pythonSymlinkCheck run")
  300.         from ConfigParser import SafeConfigParser, NoOptionError
  301.         if os.path.exists('/usr/share/python/debian_defaults'):
  302.             config = SafeConfigParser()
  303.             config.readfp(file('/usr/share/python/debian_defaults'))
  304.             try:
  305.                 expected_default = config.get('DEFAULT', 'default-version')
  306.             except NoOptionError:
  307.                 logging.debug("no default version for python found in '%s'" % config)
  308.                 return False
  309.             try:
  310.                 fs_default_version = os.readlink('/usr/bin/python')
  311.             except OSError, e:
  312.                 logging.error("os.readlink failed (%s)" % e)
  313.                 return False
  314.             if not fs_default_version in (expected_default, os.path.join('/usr/bin', expected_default)):
  315.                 logging.debug("python symlink points to: '%s', but expected is '%s' or '%s'" % (fs_default_version, expected_default, os.path.join('/usr/bin', expected_default)))
  316.                 return False
  317.         return True
  318.  
  319.  
  320.     def prepare(self):
  321.         """ initial cache opening, sanity checking, network checking """
  322.         # first check if that is a good upgrade
  323.         self.release = release = subprocess.Popen(["lsb_release","-c","-s"],
  324.                                    stdout=subprocess.PIPE).communicate()[0].strip()
  325.         logging.debug("lsb-release: '%s'" % release)
  326.         if not (release == self.fromDist or release == self.toDist):
  327.             logging.error("Bad upgrade: '%s' != '%s' " % (release, self.fromDist))
  328.             self._view.error(_("Can not upgrade"),
  329.                              _("An upgrade from '%s' to '%s' is not "
  330.                                "supported with this tool." % (release, self.toDist)))
  331.             sys.exit(1)
  332.  
  333.         # setup aufs
  334.         if self.config.getWithDefault("Aufs", "EnableFullOverlay", False):
  335.             aufs_rw_dir = self.config.get("Aufs","RWDir")
  336.             if not setupAufs(aufs_rw_dir):
  337.                 logging.error("aufs setup failed")
  338.                 self._view.error(_("Sandbox setup failed"),
  339.                                  _("It was not possible to create the sandbox "
  340.                                    "environment."))
  341.                 return False
  342.  
  343.             # all good, tell the user about the sandbox mode
  344.             logging.info("running in aufs overlay mode")
  345.             self._view.information(_("Sandbox mode"),
  346.                                    _("This upgrade is running in sandbox "
  347.                                      "(test) mode. All changes are written "
  348.                                      "to '%s' and will be lost on the next "
  349.                                      "reboot.\n\n"
  350.                                      "*No* changes written to a systemdir "
  351.                                      "from now until the next reboot are "
  352.                                      "permanent.") % aufs_rw_dir)
  353.  
  354.         # setup backports (if we have them)
  355.         if self.options and self.options.havePrerequists:
  356.             backportsdir = os.getcwd()+"/backports"
  357.             logging.info("using backports in '%s' " % backportsdir)
  358.             logging.debug("have: %s" % glob.glob(backportsdir+"/*.udeb"))
  359.             if os.path.exists(backportsdir+"/usr/bin/dpkg"):
  360.                 apt_pkg.Config.Set("Dir::Bin::dpkg",backportsdir+"/usr/bin/dpkg");
  361.             if os.path.exists(backportsdir+"/usr/lib/apt/methods"):
  362.                 apt_pkg.Config.Set("Dir::Bin::methods",backportsdir+"/usr/lib/apt/methods")
  363.             conf = backportsdir+"/etc/apt/apt.conf.d/01ubuntu"
  364.             if os.path.exists(conf):
  365.                 logging.debug("adding config '%s'" % conf)
  366.                 apt_pkg.ReadConfigFile(apt_pkg.Config, conf)
  367.  
  368.         # do the ssh check and warn if we run under ssh
  369.         self._sshMagic()
  370.         # check python version
  371.         if not self._pythonSymlinkCheck():
  372.             logging.error("pythonSymlinkCheck() failed, aborting")
  373.             self._view.error(_("Can not upgrade"),
  374.                              _("Your python install is corrupted. "
  375.                                "Please fix the '/usr/bin/python' symlink."))
  376.             sys.exit(1)
  377.         # open cache
  378.         try:
  379.             self.openCache()
  380.         except SystemError, e:
  381.             logging.error("openCache() failed: '%s'" % e)
  382.             return False
  383.         if not self.cache.sanityCheck(self._view):
  384.             return False
  385.  
  386.         # now figure out if we need to go into desktop or 
  387.         # server mode - we use a heuristic for this
  388.         self.serverMode = self.cache.needServerMode()
  389.         if self.serverMode:
  390.             os.environ["RELEASE_UPGRADE_MODE"] = "server"
  391.         else:
  392.             os.environ["RELEASE_UPGRADE_MODE"] = "desktop"
  393.  
  394.         if not self.checkViewDepends():
  395.             logging.error("checkViewDepends() failed")
  396.             return False
  397.  
  398.         if os.path.exists("/usr/bin/debsig-verify"):
  399.             logging.error("debsig-verify is installed")
  400.             self._view.error(_("Package 'debsig-verify' is installed"),
  401.                              _("The upgrade can not continue with that "
  402.                                "package installed.\n"
  403.                                "Please remove it with synaptic "
  404.                                "or 'apt-get remove debsig-verify' first "
  405.                                "and run the upgrade again."))
  406.             self.abort()
  407.  
  408.         # FIXME: we may try to find out a bit more about the network
  409.         # connection here and ask more  intelligent questions
  410.         if self.aptcdrom and self.options and self.options.withNetwork == None:
  411.             res = self._view.askYesNoQuestion(_("Include latest updates from the Internet?"),
  412.                                               _("The upgrade system can use the internet to "
  413.                                                 "automatically download "
  414.                                                 "the latest updates and install them during the "
  415.                                                 "upgrade.  If you have a network connection this is "
  416.                                                 "highly recommended.\n\n"
  417.                                                 "The upgrade will take longer, but when "
  418.                                                 "it is complete, your system will be fully up to "
  419.                                                 "date.  You can choose not to do this, but you "
  420.                                                 "should install the latest updates soon after "
  421.                                                 "upgrading.\n"
  422.                                                 "If you answer 'no' here, the network is not "
  423.                                                 "used at all."),
  424.                                               'Yes')
  425.             self.useNetwork = res
  426.             self.config.set("Options","withNetwork", str(self.useNetwork))
  427.             logging.debug("useNetwork: '%s' (selected by user)" % res)
  428.             if res:
  429.                 self._tryUpdateSelf()
  430.         return True
  431.  
  432.     def _sourcesListEntryDownloadable(self, entry):
  433.         """
  434.         helper that checks if a sources.list entry points to 
  435.         something downloadable
  436.         """
  437.         logging.debug("verifySourcesListEntry: %s" % entry)
  438.         # no way to verify without network
  439.         if not self.useNetwork:
  440.             logging.debug("skiping downloadable check (no network)")
  441.             return True
  442.         # check if the entry points to something we can download
  443.         uri = "%s/dists/%s/Release" % (entry.uri, entry.dist)
  444.         return url_downloadable(uri, logging.debug)
  445.  
  446.     def rewriteSourcesList(self, mirror_check=True):
  447.         logging.debug("rewriteSourcesList()")
  448.  
  449.         sync_components = self.config.getlist("Sources","Components")
  450.  
  451.         # skip mirror check if special environment is set
  452.         # (useful for server admins with internal repos)
  453.         if (self.config.getWithDefault("Sources","AllowThirdParty",False) or
  454.             "RELEASE_UPRADER_ALLOW_THIRD_PARTY" in os.environ):
  455.             logging.warning("mirror check skipped, *overriden* via config")
  456.             mirror_check=False
  457.  
  458.         # check if we need to enable main
  459.         if mirror_check == True and self.useNetwork:
  460.             # now check if the base-meta pkgs are available in
  461.             # the archive or only available as "now"
  462.             # -> if not that means that "main" is missing and we
  463.             #    need to  enable it
  464.             for pkgname in self.config.getlist("Distro","BaseMetaPkgs"):
  465.                 if ((not self.cache.has_key(pkgname)
  466.                      or
  467.                      len(self.cache[pkgname].candidateOrigin) == 0)
  468.                     or
  469.                     (len(self.cache[pkgname].candidateOrigin) == 1 and
  470.                      self.cache[pkgname].candidateOrigin[0].archive == "now")
  471.                    ):
  472.                     logging.debug("BaseMetaPkg '%s' has no candidateOrigin" % pkgname)
  473.                     try:
  474.                         distro = get_distro()
  475.                         distro.get_sources(self.sources)
  476.                         distro.enable_component("main")
  477.                     except NoDistroTemplateException,e :
  478.                         # fallback if everything else does not work,
  479.                         # we replace the sources.list with a single
  480.                         # line to ubuntu-main
  481.                         logging.warning('get_distro().enable_component("man") failed, overwriting sources.list instead as last resort')
  482.                         s =  "# auto generated by update-manager"
  483.                         s += "deb http://archive.ubuntu.com/ubuntu %s main restricted" % self.toDist
  484.                         s += "deb http://archive.ubuntu.com/ubuntu %s-updates main restricted" % self.toDist
  485.                         s += "deb http://security.ubuntu.com/ubuntu %s-security main restricted" % self.toDist
  486.                         open("/etc/apt/sources.list","w").write(s)
  487.                     break
  488.             
  489.         # this must map, i.e. second in "from" must be the second in "to"
  490.         # (but they can be different, so in theory we could exchange
  491.         #  component names here)
  492.         fromDists = [self.fromDist,
  493.                      self.fromDist+"-security",
  494.                      self.fromDist+"-updates",
  495.                      self.fromDist+"-proposed",
  496.                      self.fromDist+"-backports"
  497.                     ]
  498.         toDists = [self.toDist,
  499.                    self.toDist+"-security",
  500.                    self.toDist+"-updates",
  501.                    self.toDist+"-proposed",
  502.                    self.toDist+"-backports"
  503.                    ]
  504.  
  505.         self.sources_disabled = False
  506.  
  507.         # look over the stuff we have
  508.         foundToDist = False
  509.         # collect information on what components (main,universe) are enabled for what distro (sub)version
  510.         # e.g. found_components = { 'hardy':set("main","restricted"), 'hardy-updates':set("main") }
  511.         found_components = {}
  512.         for entry in self.sources.list[:]:
  513.  
  514.             # ignore invalid records or disabled ones
  515.             if entry.invalid or entry.disabled:
  516.                 continue
  517.             
  518.             # we disable breezy cdrom sources to make sure that demoted
  519.             # packages are removed
  520.             if entry.uri.startswith("cdrom:") and entry.dist == self.fromDist:
  521.                 logging.debug("disabled '%s' cdrom entry (dist == fromDist)" % entry)
  522.                 entry.disabled = True
  523.                 continue
  524.             # check if there is actually a lists file for them available
  525.             # and disable them if not
  526.             elif entry.uri.startswith("cdrom:"):
  527.                 # 
  528.                 listdir = apt_pkg.Config.FindDir("Dir::State::lists")
  529.                 if not os.path.exists("%s/%s%s_%s_%s" % 
  530.                                       (listdir,
  531.                                        apt_pkg.URItoFileName(entry.uri),
  532.                                        "dists",
  533.                                        entry.dist,
  534.                                        "Release")):
  535.                     logging.warning("disabling cdrom source '%s' because it has no  Release file" % entry)
  536.                     entry.disabled = True
  537.                 continue
  538.  
  539.             # special case for archive.canonical.com that needs to
  540.             # be rewritten (for pre-gutsy upgrades)
  541.             cdist = "%s-commercial" % self.fromDist
  542.             if (not entry.disabled and
  543.                 entry.uri.startswith("http://archive.canonical.com") and
  544.                 entry.dist == cdist):
  545.                 entry.dist = self.toDist
  546.                 entry.comps = ["partner"]
  547.                 logging.debug("transitioned commercial to '%s' " % entry)
  548.                 continue
  549.  
  550.             # special case for landscape.canonical.com because they
  551.             # don't use a standard archive layout (gutsy->hardy)
  552.             if (not entry.disabled and
  553.                 entry.uri.startswith("http://landscape.canonical.com/packages/%s" % self.fromDist)):
  554.                 logging.debug("commenting landscape.canonical.com out")
  555.                 entry.disabled = True
  556.                 continue
  557.             
  558.             # handle upgrades from a EOL release and check if there
  559.             # is a supported release available
  560.             if (not entry.disabled and
  561.                 "old-releases.ubuntu.com/" in entry.uri):
  562.                 logging.debug("upgrade from old-releases.ubuntu.com detected")
  563.                 # test country mirror first, then archive.u.c
  564.                 for uri in ["http://%sarchive.ubuntu.com/ubuntu" % country_mirror(),
  565.                             "http://archive.ubuntu.com/ubuntu"]:
  566.                     test_entry = copy.copy(entry)
  567.                     test_entry.uri = uri
  568.                     test_entry.dist = self.toDist
  569.                     if self._sourcesListEntryDownloadable(test_entry):
  570.                         logging.info("transition from old-release.u.c to %s" % uri)
  571.                         entry.uri = uri
  572.                         break
  573.  
  574.             logging.debug("examining: '%s'" % entry)
  575.             # check if it's a mirror (or official site)
  576.             validMirror = self.isMirror(entry.uri)
  577.             if validMirror or not mirror_check:
  578.                 validMirror = True
  579.                 # disabled/security/commercial are special cases
  580.                 # we use validTo/foundToDist to figure out if we have a 
  581.                 # main archive mirror in the sources.list or if we 
  582.                 # need to add one
  583.                 validTo = True
  584.                 if (entry.disabled or
  585.                     entry.type == "deb-src" or
  586.                     "/security.ubuntu.com" in entry.uri or
  587.                     "/archive.canonical.com" in entry.uri):
  588.                     validTo = False
  589.                 if entry.dist in toDists:
  590.                     # so the self.sources.list is already set to the new
  591.                     # distro
  592.                     logging.debug("entry '%s' is already set to new dist" % entry)
  593.                     foundToDist |= validTo
  594.                 elif entry.dist in fromDists:
  595.                     foundToDist |= validTo
  596.                     entry.dist = toDists[fromDists.index(entry.dist)]
  597.                     logging.debug("entry '%s' updated to new dist" % entry)
  598.                 else:
  599.                     # disable all entries that are official but don't
  600.                     # point to either "to" or "from" dist
  601.                     entry.disabled = True
  602.                     self.sources_disabled = True
  603.                     logging.debug("entry '%s' was disabled (unknown dist)" % entry)
  604.  
  605.                 # if we make it to this point, we have a official mirror
  606.  
  607.                 # check if the arch is powerpc or sparc and if so, transition
  608.                 # to ports.ubuntu.com (powerpc got demoted in gutsy, sparc
  609.                 # in hardy)
  610.                 if (entry.type == "deb" and
  611.                     not "ports.ubuntu.com" in entry.uri and
  612.                     (self.arch == "powerpc" or self.arch == "sparc")):
  613.                     logging.debug("moving %s source entry to 'ports.ubuntu.com' " % self.arch)
  614.                     entry.uri = "http://ports.ubuntu.com/ubuntu-ports/"
  615.  
  616.                 # gather what components are enabled and are inconsitent
  617.                 for d in ["%s" % self.toDist,
  618.                           "%s-updates" % self.toDist,
  619.                           "%s-security" % self.toDist]:
  620.                     # create entry if needed, ignore disabled
  621.                     # entries and deb-src
  622.                     found_components.setdefault(d,set())
  623.                     if (not entry.disabled and entry.dist == d and
  624.                         entry.type == "deb"):
  625.                         for comp in entry.comps:
  626.                             # only sync components we know about
  627.                             if not comp in sync_components:
  628.                                 continue
  629.                             found_components[d].add(comp)
  630.                     
  631.             # disable anything that is not from a official mirror
  632.             if not validMirror:
  633.                 if entry.dist == self.fromDist:
  634.                     entry.dist = self.toDist
  635.                 entry.comment += " " + _("disabled on upgrade to %s") % self.toDist
  636.                 entry.disabled = True
  637.                 self.sources_disabled = True
  638.                 logging.debug("entry '%s' was disabled (unknown mirror)" % entry)
  639.  
  640.         # now go over the list again and check for missing components 
  641.         # in $dist-updates and $dist-security and add them
  642.         for entry in self.sources.list[:]:
  643.             # skip all comps that are not relevant (including e.g. "hardy")
  644.             if (entry.invalid or entry.disabled or entry.type == "deb-src" or 
  645.                 entry.uri.startswith("cdrom:") or entry.dist == self.toDist):
  646.                 continue
  647.             # now check for "$dist-updates" and "$dist-security" and add any inconsistencies
  648.             if found_components.has_key(entry.dist):
  649.                 # add the delta between "hardy" comps and "hardy-updates" comps once
  650.                 logging.info("fixing components inconsistency from '%s'" % entry)
  651.                 entry.comps.extend(list(found_components[self.toDist]-found_components[entry.dist]))
  652.                 logging.info("to new entry '%s'" % entry)
  653.                 del found_components[entry.dist]
  654.         return foundToDist
  655.  
  656.     def updateSourcesList(self):
  657.         logging.debug("updateSourcesList()")
  658.         self.sources = SourcesList(matcherPath=".")
  659.         if not self.rewriteSourcesList(mirror_check=True):
  660.             logging.error("No valid mirror found")
  661.             res = self._view.askYesNoQuestion(_("No valid mirror found"),
  662.                              _("While scanning your repository "
  663.                                "information no mirror entry for "
  664.                                "the upgrade was found. "
  665.                                "This can happen if you run a internal "
  666.                                "mirror or if the mirror information is "
  667.                                "out of date.\n\n"
  668.                                "Do you want to rewrite your "
  669.                                "'sources.list' file anyway? If you choose "
  670.                                "'Yes' here it will update all '%s' to '%s' "
  671.                                "entries.\n"
  672.                                "If you select 'No' the upgrade will cancel."
  673.                                ) % (self.fromDist, self.toDist))
  674.             if res:
  675.                 # re-init the sources and try again
  676.                 self.sources = SourcesList(matcherPath=".")
  677.                 # its ok if rewriteSourcesList fails here if
  678.                 # we do not use a network, the sources.list may be empty
  679.                 if (not self.rewriteSourcesList(mirror_check=False)
  680.                     and self.useNetwork):
  681.                     #hm, still nothing useful ...
  682.                     prim = _("Generate default sources?")
  683.                     secon = _("After scanning your 'sources.list' no "
  684.                               "valid entry for '%s' was found.\n\n"
  685.                               "Should default entries for '%s' be "
  686.                               "added? If you select 'No', the upgrade "
  687.                               "will cancel.") % (self.fromDist, self.toDist)
  688.                     if not self._view.askYesNoQuestion(prim, secon):
  689.                         self.abort()
  690.  
  691.                     # add some defaults here
  692.                     # FIXME: find mirror here
  693.                     logging.info("generate new default sources.list")
  694.                     uri = "http://archive.ubuntu.com/ubuntu"
  695.                     comps = ["main","restricted"]
  696.                     self.sources.add("deb", uri, self.toDist, comps)
  697.                     self.sources.add("deb", uri, self.toDist+"-updates", comps)
  698.                     self.sources.add("deb",
  699.                                      "http://security.ubuntu.com/ubuntu/",
  700.                                      self.toDist+"-security", comps)
  701.             else:
  702.                 self.abort()
  703.  
  704.         # write (well, backup first ;) !
  705.         self.sources.backup(self.sources_backup_ext)
  706.         self.sources.save()
  707.  
  708.         # re-check if the written self.sources are valid, if not revert and
  709.         # bail out
  710.         # TODO: check if some main packages are still available or if we
  711.         #       accidentally shot them, if not, maybe offer to write a standard
  712.         #       sources.list?
  713.         try:
  714.             sourceslist = apt_pkg.GetPkgSourceList()
  715.             sourceslist.ReadMainList()
  716.         except SystemError:
  717.             logging.error("Repository information invalid after updating (we broke it!)")
  718.             self._view.error(_("Repository information invalid"),
  719.                              _("Upgrading the repository information "
  720.                                "resulted in a invalid file. Please "
  721.                                "report this as a bug."))
  722.             return False
  723.  
  724.         if self.sources_disabled:
  725.             self._view.information(_("Third party sources disabled"),
  726.                              _("Some third party entries in your sources.list "
  727.                                "were disabled. You can re-enable them "
  728.                                "after the upgrade with the "
  729.                                "'software-properties' tool or "
  730.                                "your package manager."
  731.                                ))
  732.         return True
  733.  
  734.     def _logChanges(self):
  735.         # debugging output
  736.         logging.debug("About to apply the following changes")
  737.         inst = []
  738.         up = []
  739.         rm = []
  740.         held = []
  741.         keep = []
  742.         for pkg in self.cache:
  743.             if pkg.markedInstall: inst.append(pkg.name)
  744.             elif pkg.markedUpgrade: up.append(pkg.name)
  745.             elif pkg.markedDelete: rm.append(pkg.name)
  746.             elif (pkg.isInstalled and pkg.isUpgradable): held.append(pkg.name)
  747.             elif pkg.isInstalled and pkg.markedKeep: keep.append(pkg.name)
  748.         logging.debug("Keep at same version: %s" % " ".join(keep))
  749.         logging.debug("Upgradable, but held- back: %s" % " ".join(held))
  750.         logging.debug("Remove: %s" % " ".join(rm))
  751.         logging.debug("Install: %s" % " ".join(inst))
  752.         logging.debug("Upgrade: %s" % " ".join(up))
  753.         
  754.  
  755.     def doPostInitialUpdate(self):
  756.         # check if we have packages in ReqReinst state that are not
  757.         # downloadable
  758.         logging.debug("doPostInitialUpdate")
  759.         self.quirks.run("PostInitialUpdate")
  760.         if len(self.cache.reqReinstallPkgs) > 0:
  761.             logging.warning("packages in reqReinstall state, trying to fix")
  762.             self.cache.fixReqReinst(self._view)
  763.             self.openCache()
  764.         if len(self.cache.reqReinstallPkgs) > 0:
  765.             reqreinst = self.cache.reqReinstallPkgs
  766.             header = ngettext("Package in inconsistent state",
  767.                               "Packages in inconsistent state",
  768.                               len(reqreinst))
  769.             summary = ngettext("The package '%s' is in an inconsistent "
  770.                                "state and needs to be reinstalled, but "
  771.                                "no archive can be found for it. "
  772.                                "Please reinstall the package manually "
  773.                                "or remove it from the system.",
  774.                                "The packages '%s' are in an inconsistent "
  775.                                "state and need to be reinstalled, but "
  776.                                "no archive can be found for them. "
  777.                                "Please reinstall the packages manually "
  778.                                "or remove them from the system.",
  779.                                len(reqreinst)) % ", ".join(reqreinst)
  780.             self._view.error(header, summary)
  781.             return False
  782.         # FIXME: check out what packages are downloadable etc to
  783.         # compare the list after the update again
  784.         self.obsolete_pkgs = self.cache._getObsoletesPkgs()
  785.         self.foreign_pkgs = self.cache._getForeignPkgs(self.origin, self.fromDist, self.toDist)
  786.         if self.serverMode:
  787.             self.tasks = self.cache.installedTasks
  788.         logging.debug("Foreign: %s" % " ".join(self.foreign_pkgs))
  789.         logging.debug("Obsolete: %s" % " ".join(self.obsolete_pkgs))
  790.         return True
  791.  
  792.     def doUpdate(self, showErrors=True, forceRetries=None):
  793.         logging.debug("running doUpdate() (showErrors=%s)" % showErrors)
  794.         if not self.useNetwork:
  795.             logging.debug("doUpdate() will not use the network because self.useNetwork==false")
  796.             return True
  797.         self.cache._list.ReadMainList()
  798.         progress = self._view.getFetchProgress()
  799.         # FIXME: also remove all files from the lists partial dir!
  800.         currentRetry = 0
  801.         if forceRetries is not None:
  802.             maxRetries=forceRetries
  803.         else:
  804.             maxRetries = self.config.getint("Network","MaxRetries")
  805.         while currentRetry < maxRetries:
  806.             try:
  807.                 res = self.cache.update(progress)
  808.             except (SystemError, IOError), e:
  809.                 logging.error("IOError/SystemError in cache.update(): '%s'. Retrying (currentRetry: %s)" % (e,currentRetry))
  810.                 currentRetry += 1
  811.                 continue
  812.             # no exception, so all was fine, we are done
  813.             return True
  814.  
  815.         logging.error("doUpdate() failed completely")
  816.         if showErrors:
  817.             self._view.error(_("Error during update"),
  818.                              _("A problem occurred during the update. "
  819.                                "This is usually some sort of network "
  820.                                "problem, please check your network "
  821.                                "connection and retry."), "%s" % e)
  822.         return False
  823.  
  824.  
  825.     def _checkFreeSpace(self):
  826.         " this checks if we have enough free space on /var and /usr"
  827.         err_sum = _("Not enough free disk space")
  828.         err_long= _("The upgrade is now aborted. "
  829.                     "The upgrade needs a total of %s free space on disk '%s'. "
  830.                     "Please free at least an additional %s of disk "
  831.                     "space on '%s'. "
  832.                     "Empty your trash and remove temporary "
  833.                     "packages of former installations using "
  834.                     "'sudo apt-get clean'.")
  835.         # allow override
  836.         if self.config.getWithDefault("FreeSpace","SkipCheck",False):
  837.             logging.warning("free space check skipped via config override")
  838.             return True
  839.         # do the check
  840.         try:
  841.             self.cache.checkFreeSpace()
  842.         except NotEnoughFreeSpaceError, e:
  843.             # ok, showing multiple error dialog sucks from the UI
  844.             # perspective, but it means we do not need to break the
  845.             # string freeze
  846.             for required in e.free_space_required_list:
  847.                 self._view.error(err_sum, err_long % (required.size_total,
  848.                                                       required.dir,
  849.                                                       required.size_needed,
  850.                                                       required.dir))
  851.             return False
  852.         return True
  853.  
  854.  
  855.     def askDistUpgrade(self):
  856.         # check what packages got demoted and ask the user
  857.         # if those shall be removed
  858.         demotions = set()
  859.         demotions_file = self.config.get("Distro","Demotions")
  860.         if os.path.exists(demotions_file):
  861.             map(lambda pkgname: demotions.add(pkgname.strip()),
  862.                 filter(lambda line: not line.startswith("#"),
  863.                        open(demotions_file).readlines()))
  864.         self.installed_demotions = [pkg.name for pkg in self.cache if pkg.isInstalled and pkg.name in demotions]
  865.         if len(self.installed_demotions) > 0:
  866.         self.installed_demotions.sort()
  867.             logging.debug("demoted: '%s'" % " ".join(self.installed_demotions))
  868.             self._view.showDemotions(_("Support for some applications ended"),
  869.                                    _("Canonical Ltd. no longer provides "
  870.                                      "support for the following software "
  871.                                      "packages. You can still get support "
  872.                                      "from the community.\n\n"
  873.                                      "If you have not enabled community "
  874.                                      "maintained software (universe), "
  875.                                      "these packages will be suggested for "
  876.                                      "removal at the end of the upgrade."),
  877.                                      self.installed_demotions)
  878.             self._view.updateStatus(_("Calculating the changes"))
  879.         # FIXME: integrate this into main upgrade dialog!?!
  880.         if not self.cache.distUpgrade(self._view, self.serverMode, self._partialUpgrade):
  881.             return False
  882.  
  883.         if self.serverMode:
  884.             if not self.cache.installTasks(self.tasks):
  885.                 return False
  886.         changes = self.cache.getChanges()
  887.         # log the changes for debugging
  888.         self._logChanges()
  889.         # check if we have enough free space 
  890.         if not self._checkFreeSpace():
  891.             return False
  892.         # ask the user if he wants to do the changes
  893.         res = self._view.confirmChanges(_("Do you want to start the upgrade?"),
  894.                                         changes,
  895.                                         self.cache.requiredDownload)
  896.         return res
  897.  
  898.     def _disableAptCronJob(self):
  899.         if os.path.exists("/etc/cron.daily/apt"):
  900.             #self._aptCronJobPerms = os.stat("/etc/cron.daily/apt")[ST_MODE]
  901.             logging.debug("disabling apt cron job (%s)" % oct(self._aptCronJobPerms))
  902.             os.chmod("/etc/cron.daily/apt",0644)
  903.     def _enableAptCronJob(self):
  904.         if os.path.exists("/etc/cron.daily/apt"):
  905.             logging.debug("enabling apt cron job")
  906.             os.chmod("/etc/cron.daily/apt", self._aptCronJobPerms)
  907.  
  908.     def doDistUpgradeFetching(self):
  909.         # ensure that no apt cleanup is run during the download/install
  910.         self._disableAptCronJob()
  911.         # get the upgrade
  912.         currentRetry = 0
  913.         fprogress = self._view.getFetchProgress()
  914.         iprogress = self._view.getInstallProgress(self.cache)
  915.         # retry the fetching in case of errors
  916.         maxRetries = self.config.getint("Network","MaxRetries")
  917.         # FIXME: we get errors like 
  918.         #   "I wasn't able to locate file for the %s package" 
  919.         #  here sometimes. its unclear why and not reproducible, the 
  920.         #  current theory is that for some reason the file is not
  921.         #  considered trusted at the moment 
  922.         #  pkgAcquireArchive::QueueNext() runs debReleaseIndex::IsTrused()
  923.         #  (the later just checks for the existence of the .gpg file)
  924.         #  OR 
  925.         #  the fact that we get a pm and fetcher here confuses something
  926.         #  in libapt?
  927.         # POSSIBLE workaround: keep the list-dir locked so that 
  928.         #          no apt-get update can run outside from the release
  929.         #          upgrader 
  930.         user_canceled = False
  931.         while currentRetry < maxRetries:
  932.             try:
  933.                 pm = apt_pkg.GetPackageManager(self.cache._depcache)
  934.                 fetcher = apt_pkg.GetAcquire(fprogress)
  935.                 res = self.cache._fetchArchives(fetcher, pm)
  936.             except apt.cache.FetchCancelledException, e:
  937.                 logging.info("user canceled")
  938.                 user_canceled = True
  939.                 break
  940.             except IOError, e:
  941.                 # fetch failed, will be retried
  942.                 logging.error("IOError in cache.commit(): '%s'. Retrying (currentTry: %s)" % (e,currentRetry))
  943.                 currentRetry += 1
  944.                 continue
  945.             return True
  946.         
  947.         # maximum fetch-retries reached without a successful commit
  948.         if user_canceled:
  949.             self._view.information(_("Upgrade canceled"),
  950.                                    _("The upgrade cancels now and the original "
  951.                                      "system state is restored. You can resume "
  952.                                      "the upgrade at a later time."))
  953.         else:
  954.             logging.error("giving up on fetching after maximum retries")
  955.             self._view.error(_("Could not download the upgrades"),
  956.                              _("The upgrade is now aborted. Please check your "
  957.                                "Internet connection or "
  958.                                "installation media and try again. All files "
  959.                                "downloaded so far are kept."),
  960.                              "%s" % e)
  961.         # abort here because we want our sources.list back
  962.         self._enableAptCronJob()
  963.         self.abort()
  964.  
  965.     def enableApport(self, fname="/etc/default/apport"):
  966.         " enable apoprt "
  967.         # for jaunty and later we could use this instead:
  968.         #  env = copy.copy(os.environ)
  969.         #  env["force_start"] = "1"
  970.         #  subprocess.call(["/etc/init.d/apport","start"], env=env)
  971.         # but hardy and intrepid do not have the force_start yet
  972.         if not (os.path.exists(fname) and os.path.exists("etc-default-apport")):
  973.             return
  974.         # copy the jaunty version of the conf file in place
  975.         # (this avoids a conffile prompt later)
  976.         logging.debug("enabling apport")
  977.         shutil.copy("etc-default-apport","/etc/default/apport")
  978.         subprocess.call(["/etc/init.d/apport","start"])
  979.         
  980.     def doDistUpgrade(self):
  981.         # check if we want apport running during the upgrade
  982.         if self.config.getWithDefault("Distro","EnableApport", False):
  983.             self.enableApport()
  984.         # get the upgrade
  985.         currentRetry = 0
  986.         fprogress = self._view.getFetchProgress()
  987.         iprogress = self._view.getInstallProgress(self.cache)
  988.         # retry the fetching in case of errors
  989.         maxRetries = self.config.getint("Network","MaxRetries")
  990.         if not self._partialUpgrade:
  991.             self.quirks.run("StartUpgrade")
  992.         while currentRetry < maxRetries:
  993.             try:
  994.                 res = self.cache.commit(fprogress,iprogress)
  995.             except SystemError, e:
  996.                 logging.error("SystemError from cache.commit(): %s" % e)
  997.                 # if its a ordering bug we can cleanly revert to
  998.                 # the previous release, no packages have been installed
  999.                 # yet (LP: #328655, #356781)
  1000.                 pre_configure_errors = [
  1001.                   "E:Internal Error, Could not perform immediate configuration",
  1002.                   "E:Couldn't configure pre-depend "]
  1003.                 for preconf_error in pre_configure_errors:
  1004.                     if str(e).startswith(preconf_error):
  1005.                         logging.debug("detected preconfigure error, restorting state")
  1006.                         self._enableAptCronJob()
  1007.                         # FIXME: strings are not good, but we are in string freeze
  1008.                         # currently
  1009.                         msg = _("Error during commit")
  1010.                         msg += "\n'%s'\n" % str(e)
  1011.                         msg += _("Restoring original system state")
  1012.                         self._view.error(_("Could not install the upgrades"), msg)
  1013.                         # abort() exits cleanly
  1014.                         self.abort()
  1015.                 
  1016.                 # invoke the frontend now and show a error message
  1017.                 msg = _("The upgrade is now aborted. Your system "
  1018.                         "could be in an unusable state. A recovery "
  1019.                         "will run now (dpkg --configure -a).")
  1020.                 if not self._partialUpgrade:
  1021.                     if not run_apport():
  1022.                         msg += _("\n\nPlease report this bug against the 'update-manager' "
  1023.                                  "package and include the files in /var/log/dist-upgrade/ "
  1024.                                  "in the bug report.\n"
  1025.                                  "%s" % e)
  1026.                 self._view.error(_("Could not install the upgrades"), msg)
  1027.                 # installing the packages failed, can't be retried
  1028.                 self._view.getTerminal().call(["dpkg","--configure","-a"])
  1029.                 self._enableAptCronJob()
  1030.                 return False
  1031.             except IOError, e:
  1032.                 # fetch failed, will be retried
  1033.                 logging.error("IOError in cache.commit(): '%s'. Retrying (currentTry: %s)" % (e,currentRetry))
  1034.                 currentRetry += 1
  1035.                 continue
  1036.             # no exception, so all was fine, we are done
  1037.             self._enableAptCronJob()
  1038.             return True
  1039.         
  1040.         # maximum fetch-retries reached without a successful commit
  1041.         logging.error("giving up on fetching after maximum retries")
  1042.         self._view.error(_("Could not download the upgrades"),
  1043.                          _("The upgrade is now aborted. Please check your "\
  1044.                            "Internet connection or "\
  1045.                            "installation media and try again. "),
  1046.                            "%s" % e)
  1047.         # abort here because we want our sources.list back
  1048.         self.abort()
  1049.  
  1050.     def doPostUpgrade(self):
  1051.         # reopen cache
  1052.         self.openCache()
  1053.         # run the quirks handler that does does like things adding
  1054.         # missing groups or similar work arounds, only do it on real
  1055.         # upgrades
  1056.         self.quirks.run("PostUpgrade")
  1057.         # check out what packages are cruft now
  1058.         # use self.{foreign,obsolete}_pkgs here and see what changed
  1059.         now_obsolete = self.cache._getObsoletesPkgs()
  1060.         now_foreign = self.cache._getForeignPkgs(self.origin, self.fromDist, self.toDist)
  1061.         logging.debug("Obsolete: %s" % " ".join(now_obsolete))
  1062.         logging.debug("Foreign: %s" % " ".join(now_foreign))
  1063.         # now sanity check - if a base meta package is in the obsolete list now, that means
  1064.         # that something went wrong (see #335154) badly with the network. this should never happen, but it did happen
  1065.         # at least once so we add extra paranoia here
  1066.         for pkg in self.config.getlist("Distro","BaseMetaPkgs"):
  1067.             if pkg in now_obsolete:
  1068.                 logging.error("the BaseMetaPkg '%s' is in the obsolete list, something is wrong, ignoring the obsoletes" % pkg)
  1069.                 now_obsolete = set()
  1070.                 break
  1071.         # check if we actually want obsolete removal
  1072.         if not self.config.getWithDefault("Distro","RemoveObsoletes", True):
  1073.             logging.debug("Skipping obsolete Removal")
  1074.             return True
  1075.  
  1076.         # now get the meta-pkg specific obsoletes and purges
  1077.         for pkg in self.config.getlist("Distro","MetaPkgs"):
  1078.             if self.cache.has_key(pkg) and self.cache[pkg].isInstalled:
  1079.                 self.forced_obsoletes.extend(self.config.getlist(pkg,"ForcedObsoletes"))
  1080.         # now add the obsolete kernels to the forced obsoletes
  1081.         self.forced_obsoletes.extend(self.cache.identifyObsoleteKernels())
  1082.         logging.debug("forced_obsoletes: %s", self.forced_obsoletes)
  1083.  
  1084.         # mark packages that are now obsolete (and where not obsolete
  1085.         # before) to be deleted. make sure to not delete any foreign
  1086.         # (that is, not from ubuntu) packages
  1087.         if self.useNetwork:
  1088.             # we can only do the obsoletes calculation here if we use a
  1089.             # network. otherwise after rewriting the sources.list everything
  1090.             # that is not on the CD becomes obsolete (not-downloadable)
  1091.             remove_candidates = now_obsolete - self.obsolete_pkgs
  1092.         else:
  1093.             # initial remove candidates when no network is used should
  1094.             # be the demotions to make sure we don't leave potential
  1095.             # unsupported software
  1096.             remove_candidates = set(self.installed_demotions)
  1097.         remove_candidates |= set(self.forced_obsoletes)
  1098.  
  1099.         # no go for the unused dependencies
  1100.         unused_dependencies = self.cache._getUnusedDependencies()
  1101.         logging.debug("Unused dependencies: %s" %" ".join(unused_dependencies))
  1102.         remove_candidates |= set(unused_dependencies)
  1103.  
  1104.         # see if we actually have to do anything here
  1105.         if not self.config.getWithDefault("Distro","RemoveObsoletes", True):
  1106.             logging.debug("Skipping RemoveObsoletes as stated in the config")
  1107.             remove_candidates = set()
  1108.         logging.debug("remove_candidates: '%s'" % remove_candidates)
  1109.         logging.debug("Start checking for obsolete pkgs")
  1110.         for pkgname in remove_candidates:
  1111.             if pkgname not in self.foreign_pkgs:
  1112.                 self._view.processEvents()
  1113.                 if not self.cache.tryMarkObsoleteForRemoval(pkgname, remove_candidates, self.foreign_pkgs):
  1114.                     logging.debug("'%s' scheduled for remove but not safe to remove, skipping", pkgname)
  1115.         logging.debug("Finish checking for obsolete pkgs")
  1116.  
  1117.         # get changes
  1118.         changes = self.cache.getChanges()
  1119.         logging.debug("The following packages are marked for removal: %s" % " ".join([pkg.name for pkg in changes]))
  1120.         summary = _("Remove obsolete packages?")
  1121.         actions = [_("_Keep"), _("_Remove")]
  1122.         # FIXME Add an explanation about what obsolete packages are
  1123.         #explanation = _("")
  1124.         if (len(changes) > 0 and 
  1125.             self._view.confirmChanges(summary, changes, 0, actions, False)):
  1126.             fprogress = self._view.getFetchProgress()
  1127.             iprogress = self._view.getInstallProgress(self.cache)
  1128.             try:
  1129.                 res = self.cache.commit(fprogress,iprogress)
  1130.             except (SystemError, IOError), e:
  1131.                 logging.error("cache.commit() in doPostUpgrade() failed: %s" % e)
  1132.                 self._view.error(_("Error during commit"),
  1133.                                  _("A problem occurred during the clean-up. "
  1134.                                    "Please see the below message for more "
  1135.                                    "information. "),
  1136.                                    "%s" % e)
  1137.         # run stuff after cleanup
  1138.         self.quirks.run("PostCleanup")
  1139.         # run the post upgrade scripts that can do fixup like xorg.conf
  1140.         # fixes etc - only do on real upgrades
  1141.         if not self._partialUpgrade:
  1142.             self.runPostInstallScripts()
  1143.         return True
  1144.  
  1145.     def runPostInstallScripts(self):
  1146.         """ 
  1147.         scripts that are run in any case after the distupgrade finished 
  1148.         whether or not it was successfull
  1149.         """
  1150.         # now run the post-upgrade fixup scripts (if any)
  1151.         for script in self.config.getlist("Distro","PostInstallScripts"):
  1152.             if not os.path.exists(script):
  1153.                 logging.warning("PostInstallScript: '%s' not found" % script)
  1154.                 continue
  1155.             logging.debug("Running PostInstallScript: '%s'" % script)
  1156.             try:
  1157.                 # work around kde tmpfile problem where it eats permissions
  1158.                 check_and_fix_xbit(script)
  1159.                 self._view.getTerminal().call([script], hidden=True)
  1160.             except Exception, e:
  1161.                 logging.error("got error from PostInstallScript %s (%s)" % (script, e))
  1162.         
  1163.     def abort(self):
  1164.         """ abort the upgrade, cleanup (as much as possible) """
  1165.         logging.debug("abort called")
  1166.         if hasattr(self, "sources"):
  1167.             self.sources.restoreBackup(self.sources_backup_ext)
  1168.         if hasattr(self, "aptcdrom"):
  1169.             self.aptcdrom.restoreBackup(self.sources_backup_ext)
  1170.         # generate a new cache
  1171.         self._view.updateStatus(_("Restoring original system state"))
  1172.         self._view.abort()
  1173.         self.openCache()
  1174.         sys.exit(1)
  1175.  
  1176.     def _checkDep(self, depstr):
  1177.         " check if a given depends can be satisfied "
  1178.         for or_group in apt_pkg.ParseDepends(depstr):
  1179.             logging.debug("checking: '%s' " % or_group)
  1180.             for dep in or_group:
  1181.                 depname = dep[0]
  1182.                 ver = dep[1]
  1183.                 oper = dep[2]
  1184.                 if not self.cache.has_key(depname):
  1185.                     logging.error("_checkDep: '%s' not in cache" % depname)
  1186.                     return False
  1187.                 inst = self.cache[depname]
  1188.                 instver = inst.installedVersion
  1189.                 if (instver != None and
  1190.                     apt_pkg.CheckDep(instver,oper,ver) == True):
  1191.                     return True
  1192.         logging.error("depends '%s' is not satisfied" % depstr)
  1193.         return False
  1194.                 
  1195.     def checkViewDepends(self):
  1196.         " check if depends are satisfied "
  1197.         logging.debug("checkViewDepends()")
  1198.         res = True
  1199.         # now check if anything from $foo-updates is required
  1200.         depends = self.config.getlist("View","Depends")
  1201.         depends.extend(self.config.getlist(self._view.__class__.__name__,
  1202.                                            "Depends"))
  1203.         for dep in depends:
  1204.             logging.debug("depends: '%s'", dep)
  1205.             res &= self._checkDep(dep)
  1206.             if not res:
  1207.                 # FIXME: instead of error out, fetch and install it
  1208.                 #        here
  1209.                 self._view.error(_("Required depends is not installed"),
  1210.                                  _("The required dependency '%s' is not "
  1211.                                    "installed. " % dep))
  1212.                 sys.exit(1)
  1213.         return res 
  1214.  
  1215.     def _verifyBackports(self):
  1216.         # run update (but ignore errors in case the countrymirror
  1217.         # substitution goes wrong, real errors will be caught later
  1218.         # when the cache is searched for the backport packages)
  1219.         backportslist = self.config.getlist("PreRequists","Packages")
  1220.         i=0
  1221.         noCache = apt_pkg.Config.Find("Acquire::http::No-Cache","false")
  1222.         maxRetries = self.config.getint("Network","MaxRetries")
  1223.         while i < maxRetries:
  1224.             self.doUpdate(showErrors=False)
  1225.             self.openCache()
  1226.             for pkgname in backportslist:
  1227.                 if not self.cache.has_key(pkgname):
  1228.                     logging.error("Can not find backport '%s'" % pkgname)
  1229.                     raise NoBackportsFoundException, pkgname
  1230.             if self._allBackportsAuthenticated(backportslist):
  1231.                 break
  1232.             # FIXME: move this to some more generic place
  1233.             logging.debug("setting a cache control header to turn off caching temporarily")
  1234.             apt_pkg.Config.Set("Acquire::http::No-Cache","true")
  1235.             i += 1
  1236.         if i == maxRetries:
  1237.             logging.error("pre-requists item is NOT trusted, giving up")
  1238.             return False
  1239.         apt_pkg.Config.Set("Acquire::http::No-Cache",noCache)
  1240.         return True
  1241.  
  1242.     def _allBackportsAuthenticated(self, backportslist):
  1243.         # check if the user overwrote the check
  1244.         if apt_pkg.Config.FindB("APT::Get::AllowUnauthenticated",False) == True:
  1245.             logging.warning("skip authentication check because of APT::Get::AllowUnauthenticated==true")
  1246.             return True
  1247.         try:
  1248.             b = self.config.getboolean("Distro","AllowUnauthenticated")
  1249.             if b:
  1250.                 return True
  1251.         except ConfigParser.NoOptionError, e:
  1252.             pass
  1253.         for pkgname in backportslist:
  1254.             pkg = self.cache[pkgname]                
  1255.             for cand in pkg.candidateOrigin:
  1256.                 if cand.trusted:
  1257.                     break
  1258.             else:
  1259.                 return False
  1260.         return True
  1261.  
  1262.     def isMirror(self, uri):
  1263.         " check if uri is a known mirror "
  1264.         uri = uri.rstrip("/")
  1265.         for mirror in self.valid_mirrors:
  1266.             mirror = mirror.rstrip("/")
  1267.             if is_mirror(mirror, uri):
  1268.                 return True
  1269.             # deal with mirrors like
  1270.             #    deb http://localhost:9977/security.ubuntu.com/ubuntu intrepid-security main restricted
  1271.             # both apt-debtorrent and apt-cacher use this (LP: #365537)
  1272.             mirror_host_part = mirror.split("//")[1]
  1273.             if uri.endswith(mirror_host_part):
  1274.                 logging.debug("found apt-cacher/apt-torrent style uri %s" % uri)
  1275.                 return True
  1276.         return False
  1277.  
  1278.     def _getPreReqMirrorLines(self, dumb=False):
  1279.         " get sources.list snippet lines for the current mirror "
  1280.         lines = ""
  1281.         sources = SourcesList(matcherPath=".")
  1282.         for entry in sources.list:
  1283.             if entry.invalid or entry.disabled:
  1284.                 continue
  1285.             if (entry.type == "deb" and 
  1286.                 self.isMirror(entry.uri) and
  1287.                 not entry.uri.startswith("http://security.ubuntu.com") and
  1288.                 not entry.uri.startswith("http://archive.ubuntu.com") ):
  1289.                 new_line = "deb %s %s-backports main/debian-installer\n" % (entry.uri, self.fromDist)
  1290.                 if not new_line in lines:
  1291.                     lines += new_line
  1292.             if (dumb and entry.type == "deb" and
  1293.                 "main" in entry.comps):
  1294.                 lines += "deb %s %s-backports main/debian-installer\n" % (entry.uri, self.fromDist)
  1295.         return lines
  1296.  
  1297.     def _addPreRequistsSourcesList(self, template, out, dumb=False):
  1298.         " add prerequists based on template into the path outfile "
  1299.         # go over the sources.list and try to find a valid mirror
  1300.         # that we can use to add the backports dir
  1301.         logging.debug("writing prerequists sources.list at: '%s' " % out)
  1302.         outfile = open(out, "w")
  1303.         mirrorlines = self._getPreReqMirrorLines(dumb)
  1304.         for line in open(template):
  1305.             template = Template(line)
  1306.             outline = template.safe_substitute(mirror=mirrorlines)
  1307.             outfile.write(outline)
  1308.             logging.debug("adding '%s' prerequists" % outline)
  1309.         outfile.close()
  1310.         return True
  1311.  
  1312.     def getRequiredBackports(self):
  1313.         " download the backports specified in DistUpgrade.cfg "
  1314.         logging.debug("getRequiredBackports()")
  1315.         res = True
  1316.         backportsdir = os.path.join(os.getcwd(),"backports")
  1317.         if not os.path.exists(backportsdir):
  1318.             os.mkdir(backportsdir)
  1319.         backportslist = self.config.getlist("PreRequists","Packages")
  1320.  
  1321.         # if we have them on the CD we are fine
  1322.         if self.aptcdrom and not self.useNetwork:
  1323.             logging.debug("Searching for pre-requists on CDROM")
  1324.             p = os.path.join(self.aptcdrom.cdrompath,
  1325.                              "dists/stable/main/dist-upgrader/binary-%s/" % apt_pkg.Config.Find("APT::Architecture"))
  1326.             found_pkgs = set()
  1327.             for udeb in glob.glob(p+"*_*.udeb"):
  1328.                 logging.debug("copying pre-req '%s' to '%s'" % (udeb, backportsdir))
  1329.                 found_pkgs.add(os.path.basename(udeb).split("_")[0])
  1330.                 shutil.copy(udeb, backportsdir)
  1331.             # now check if we got all backports on the CD
  1332.             if not set(backportslist) == found_pkgs:
  1333.                 logging.error("Expected backports: '%s' but got '%s'" % (set(backportslist), found_pkgs))
  1334.                 return False
  1335.             return self.setupRequiredBackports(backportsdir)
  1336.  
  1337.         # we support PreRequists/SourcesList-$arch sections here too
  1338.         # 
  1339.         # logic for mirror finding works list this:     
  1340.         # - use the mirror template from the config, then: [done]
  1341.         # 
  1342.         #  - try to find known mirror (isMirror) and prepend it [done]
  1343.         #  - archive.ubuntu.com is always a fallback at the end [done]
  1344.         # 
  1345.         # see if we find backports with that
  1346.         # - if not, try guessing based on URI, Trust and Dist   [done]
  1347.         #   in existing sources.list (internal mirror with no
  1348.         #   outside connection maybe)
  1349.         # 
  1350.         # make sure to remove file on cancel
  1351.         
  1352.         # FIXME: use the DistUpgradeFetcherCore logic
  1353.         #        in mirror_from_sources_list() here
  1354.         #        (and factor that code out into a helper)
  1355.  
  1356.         conf_option = "SourcesList"
  1357.         if self.config.has_option("PreRequists",conf_option+"-%s" % self.arch):
  1358.             conf_option = conf_option + "-%s" % self.arch
  1359.         prereq_template = self.config.get("PreRequists",conf_option)
  1360.         if not os.path.exists(prereq_template):
  1361.             logging.error("sourceslist not found '%s'" % prereq_template)
  1362.             return False
  1363.         outpath = os.path.join(apt_pkg.Config.FindDir("Dir::Etc::sourceparts"), prereq_template)
  1364.         outfile = os.path.join(apt_pkg.Config.FindDir("Dir::Etc::sourceparts"), prereq_template)
  1365.         self._addPreRequistsSourcesList(prereq_template, outfile) 
  1366.         try:
  1367.             self._verifyBackports()
  1368.         except NoBackportsFoundException, e:
  1369.             self._addPreRequistsSourcesList(prereq_template, outfile, dumb=True) 
  1370.             try:
  1371.                 self._verifyBackports()
  1372.             except NoBackportsFoundException, e:
  1373.                 logging.warning("no backport for '%s' found" % e)
  1374.             return False
  1375.         
  1376.         # save cachedir and setup new one
  1377.         cachedir = apt_pkg.Config.Find("Dir::Cache::archives")
  1378.         cwd = os.getcwd()
  1379.         if not os.path.exists(os.path.join(backportsdir,"partial")):
  1380.             os.mkdir(os.path.join(backportsdir,"partial"))
  1381.         os.chdir(backportsdir)
  1382.         apt_pkg.Config.Set("Dir::Cache::archives",backportsdir)
  1383.  
  1384.         # FIXME: sanity check the origin (just for safety)
  1385.         for pkgname in backportslist:
  1386.             pkg = self.cache[pkgname]
  1387.             # look for the right version (backport)
  1388.             ver = self.cache._depcache.GetCandidateVer(pkg._pkg)
  1389.             if not ver:
  1390.                 logging.error("No candidate for '%s'" % pkgname)
  1391.                 os.unlink(outpath)
  1392.                 return False
  1393.             if ver.FileList == None:
  1394.                 logging.error("No ver.FileList for '%s'" % pkgname)
  1395.                 os.unlink(outpath)
  1396.                 return False
  1397.             logging.debug("marking '%s' for install" % pkgname)
  1398.             # mvo: autoInst is not available on dapper
  1399.             #pkg.markInstall(autoInst=False, autoFix=False)
  1400.             pkg.markInstall(autoFix=False)
  1401.  
  1402.         # mark the backports for upgrade and get them
  1403.         fetcher = apt_pkg.GetAcquire(self._view.getFetchProgress())
  1404.         pm = apt_pkg.GetPackageManager(self.cache._depcache)
  1405.  
  1406.         # now get it
  1407.         try:
  1408.             res = True
  1409.             self.cache._fetchArchives(fetcher, pm)
  1410.         except IOError, e:
  1411.             logging.error("_fetchArchives returned '%s'" % e)
  1412.             res = False
  1413.  
  1414.         if res == False:
  1415.             logging.warning("_fetchArchives for backports returned False")
  1416.  
  1417.         # reset the cache dir
  1418.         os.unlink(outpath)
  1419.         apt_pkg.Config.Set("Dir::Cache::archives",cachedir)
  1420.         os.chdir(cwd)
  1421.         return self.setupRequiredBackports(backportsdir)
  1422.  
  1423.     def setupRequiredBackports(self, backportsdir):
  1424.         " setup the required backports in a evil way "
  1425.         if not glob.glob(backportsdir+"/*.udeb"):
  1426.             logging.error("no backports found in setupRequiredBackports()")
  1427.             return False
  1428.         # unpack the backports first
  1429.         for deb in glob.glob(backportsdir+"/*.udeb"):
  1430.             logging.debug("extracting udeb '%s' " % deb)
  1431.             if os.system("dpkg-deb -x %s %s" % (deb, backportsdir)) != 0:
  1432.                 return False
  1433.         # setup some paths to make sure the new stuff is used
  1434.         os.environ["LD_LIBRARY_PATH"] = backportsdir+"/usr/lib"
  1435.         os.environ["PYTHONPATH"] = backportsdir+"/usr/lib/python%s.%s/site-packages/" % (sys.version_info[0], sys.version_info[1])
  1436.         os.environ["PATH"] = "%s:%s" % (backportsdir+"/usr/bin",
  1437.                                         os.environ["PATH"])
  1438.         # copy log so that it gets not overwritten
  1439.         logging.shutdown()
  1440.         shutil.copy("/var/log/dist-upgrade/main.log",
  1441.                     "/var/log/dist-upgrade/main_pre_req.log")
  1442.         # now exec self again
  1443.         args = sys.argv + ["--have-prerequists"]
  1444.         if self.useNetwork:
  1445.             args.append("--with-network")
  1446.         else:
  1447.             args.append("--without-network")
  1448.         # work around kde being clever and removing the x bit
  1449.         check_and_fix_xbit(sys.argv[0])
  1450.         os.execve(sys.argv[0],args, os.environ)
  1451.  
  1452.     # this is the core
  1453.     def fullUpgrade(self):
  1454.         # sanity check (check for ubuntu-desktop, brokenCache etc)
  1455.         self._view.updateStatus(_("Checking package manager"))
  1456.         self._view.setStep(DistUpgradeView.STEP_PREPARE)
  1457.  
  1458.         if not self.prepare():
  1459.             logging.error("self.prepared() failed")
  1460.             self._view.error(_("Preparing the upgrade failed"),
  1461.                              _("Preparing the system for the upgrade "
  1462.                                "failed. Please report this as a bug "
  1463.                                "against the 'update-manager' "
  1464.                                "package and include the files in "
  1465.                                "/var/log/dist-upgrade/ "
  1466.                                "in the bug report." ))
  1467.             sys.exit(1)
  1468.  
  1469.         # mvo: commented out for now, see #54234, this needs to be
  1470.         #      refactored to use a arch=any tarball
  1471.         if (self.config.has_section("PreRequists") and
  1472.             self.options and
  1473.             self.options.havePrerequists == False):
  1474.             logging.debug("need backports")
  1475.             # get backported packages (if needed)
  1476.             if not self.getRequiredBackports():
  1477.                 self._view.error(_("Getting upgrade prerequisites failed"),
  1478.                                  _("The system was unable to get the "
  1479.                                    "prerequisites for the upgrade. "
  1480.                                    "The upgrade will abort now and restore "
  1481.                                    "the original system state.\n"
  1482.                                    "\n"
  1483.                                    "Please report this as a bug "
  1484.                                    "against the 'update-manager' "
  1485.                                    "package and include the files in "
  1486.                                    "/var/log/dist-upgrade/ "
  1487.                                    "in the bug report." ))
  1488.                 self.abort()
  1489.  
  1490.         # run a "apt-get update" now, its ok to ignore errors, 
  1491.         # because 
  1492.         # a) we disable any third party sources later
  1493.         # b) we check if we have valid ubuntu sources later
  1494.         #    after we rewrite the sources.list and do a 
  1495.         #    apt-get update there too
  1496.         # because the (unmodified) sources.list of the user
  1497.         # may contain bad/unreachable entries we run only
  1498.         # with a single retry
  1499.         self.doUpdate(showErrors=False, forceRetries=1)
  1500.         self.openCache()
  1501.  
  1502.         # do pre-upgrade stuff (calc list of obsolete pkgs etc)
  1503.         if not self.doPostInitialUpdate():
  1504.             self.abort()
  1505.  
  1506.         # update sources.list
  1507.         self._view.setStep(DistUpgradeView.STEP_MODIFY_SOURCES)
  1508.         self._view.updateStatus(_("Updating repository information"))
  1509.         if not self.updateSourcesList():
  1510.             self.abort()
  1511.  
  1512.         # add cdrom (if we have one)
  1513.         if (self.aptcdrom and
  1514.             not self.aptcdrom.add(self.sources_backup_ext)):
  1515.             sys.exit(1)
  1516.  
  1517.         # then update the package index files
  1518.         if not self.doUpdate():
  1519.             self.abort()
  1520.  
  1521.         # then open the cache (again)
  1522.         self._view.updateStatus(_("Checking package manager"))
  1523.         self.openCache()
  1524.         # now check if we still have some key packages available/downloadable
  1525.         # after the update - if not something went seriously wrong
  1526.         # (this happend e.g. during the intrepid->jaunty upgrade for some
  1527.         #  users when de.archive.ubuntu.com was overloaded)
  1528.         for pkg in self.config.getlist("Distro","BaseMetaPkgs"):
  1529.             if (not self.cache.has_key(pkg) or
  1530.                 not self.cache.anyVersionDownloadable(self.cache[pkg])):
  1531.                 # FIXME: we could offer to add default source entries here,
  1532.                 #        but we need to be careful to not duplicate them
  1533.                 #        (i.e. the error here could be something else than
  1534.                 #        missing sources entries but network errors etc)
  1535.                 logging.error("No '%s' available/downloadable after sources.list rewrite+update" % pkg) 
  1536.                 self._view.error(_("Invalid package information"),
  1537.                                  _("After your package information was "
  1538.                                    "updated the essential package '%s' can "
  1539.                                    "not be found anymore.\n"
  1540.                                    "This indicates a serious error, please "
  1541.                                    "report this bug against the 'update-manager' "
  1542.                                    "package and include the files in /var/log/dist-upgrade/ "
  1543.                                    "in the bug report.") % pkg)
  1544.                 self.abort()
  1545.  
  1546.         # calc the dist-upgrade and see if the removals are ok/expected
  1547.         # do the dist-upgrade
  1548.         self._view.updateStatus(_("Calculating the changes"))
  1549.         if not self.askDistUpgrade():
  1550.             self.abort()
  1551.  
  1552.         # fetch the stuff
  1553.         self._view.setStep(DistUpgradeView.STEP_FETCH)
  1554.         self._view.updateStatus(_("Fetching"))
  1555.         if not self.doDistUpgradeFetching():
  1556.             self.abort()
  1557.  
  1558.         # now do the upgrade
  1559.         self._view.setStep(DistUpgradeView.STEP_INSTALL)
  1560.         self._view.updateStatus(_("Upgrading"))
  1561.         if not self.doDistUpgrade():
  1562.             # run the post install scripts (for stuff like UUID conversion)
  1563.             self.runPostInstallScripts()
  1564.             # don't abort here, because it would restore the sources.list
  1565.             self._view.information(_("Upgrade complete"),
  1566.                                    _("The upgrade is completed but there "
  1567.                                      "were errors during the upgrade "
  1568.                                      "process."))
  1569.             sys.exit(1) 
  1570.             
  1571.         # do post-upgrade stuff
  1572.         self._view.setStep(DistUpgradeView.STEP_CLEANUP)
  1573.         self._view.updateStatus(_("Searching for obsolete software"))
  1574.         self.doPostUpgrade()
  1575.  
  1576.         # done, ask for reboot
  1577.         self._view.setStep(DistUpgradeView.STEP_REBOOT)
  1578.         self._view.updateStatus(_("System upgrade is complete."))            
  1579.         # FIXME should we look into /var/run/reboot-required here?
  1580.         if self._view.confirmRestart():
  1581.             p = subprocess.Popen("/sbin/reboot")
  1582.             sys.exit(0)
  1583.         return True
  1584.         
  1585.     def run(self):
  1586.         self._view.processEvents()
  1587.         return self.fullUpgrade()
  1588.     
  1589.     def doPartialUpgrade(self):
  1590.         " partial upgrade mode, useful for repairing "
  1591.         from DistUpgrade.DistUpgradeView import STEP_PREPARE, STEP_MODIFY_SOURCES, STEP_FETCH, STEP_INSTALL, STEP_CLEANUP, STEP_REBOOT
  1592.         self._view.setStep(STEP_PREPARE)
  1593.         self._view.hideStep(STEP_MODIFY_SOURCES)
  1594.         self._view.hideStep(STEP_REBOOT)
  1595.         self._partialUpgrade = True
  1596.         self.prepare()
  1597.         if not self.doPostInitialUpdate():
  1598.             return False
  1599.         if not self.askDistUpgrade():
  1600.             return False
  1601.         self._view.setStep(STEP_FETCH)
  1602.         self._view.updateStatus(_("Fetching"))
  1603.         if not self.doDistUpgradeFetching():
  1604.             return False
  1605.         self._view.setStep(STEP_INSTALL)
  1606.         self._view.updateStatus(_("Upgrading"))
  1607.         if not self.doDistUpgrade():
  1608.             self._view.information(_("Upgrade complete"),
  1609.                                    _("The upgrade is completed but there "
  1610.                                      "were errors during the upgrade "
  1611.                                      "process."))
  1612.             return False
  1613.         self._view.setStep(STEP_CLEANUP)
  1614.         if not self.doPostUpgrade():
  1615.             self._view.information(_("Upgrade complete"),
  1616.                                    _("The upgrade is completed but there "
  1617.                                      "were errors during the upgrade "
  1618.                                      "process."))
  1619.             return False
  1620.         self._view.information(_("Upgrade complete"),
  1621.                                _("The partial upgrade was completed."))
  1622.         return True
  1623.  
  1624.  
  1625. if __name__ == "__main__":
  1626.     from DistUpgradeView import DistUpgradeView
  1627.     from DistUpgradeViewText import DistUpgradeViewText
  1628.     from DistUpgradeCache import MyCache
  1629.     logging.basicConfig(level=logging.DEBUG)
  1630.     v = DistUpgradeViewText()
  1631.     dc = DistUpgradeController(v)
  1632.     #dc.openCache()
  1633.     dc._disableAptCronJob()
  1634.     dc._enableAptCronJob()
  1635.     #dc._addRelatimeToFstab()
  1636.     #dc.prepare()
  1637.     #dc.askDistUpgrade()
  1638.     #dc._checkFreeSpace()
  1639.     #dc._rewriteFstab()
  1640.     #dc._checkAdminGroup()
  1641.     #dc._rewriteAptPeriodic(2)
  1642.